1 <?php
2
3 define(
'datalist_filters_count', 20);
4 define(
'datalist_image_uploads_exist', false);
5 define(
'datalist_max_records_multi_selection', 1000);
6 define(
'datalist_max_page_lump', 50);
7 define(
'datalist_max_records_dv_print', 100);
8 define(
'datalist_auto_complete_size', 1000);
9 define(
'datalist_date_separator', '/');
10 define(
'datalist_date_format', 'mdY');
11
12 $curr_dir = dirname(__FILE__);
13 require_once($curr_dir .
'/combo.class.php');
14 require_once($curr_dir .
'/data_combo.class.php');
15 require_once($curr_dir .
'/date_combo.class.php');
16
17 class
DataList{
18     
// this class generates the data table ...
19
20     
var $QueryFieldsTV,
21         $QueryFieldsCSV,
22         $QueryFieldsFilters,
23         $QueryFieldsQS,
24         $QueryFrom,
25         $QueryWhere,
26         $QueryOrder,
27         $filterers,
28
29         $ColWidth,
// array of field widths
30         $DataHeight,
31         $TableName,
32
33         $AllowSelection,
34         $AllowDelete,
35         $AllowMassDelete,
36         $AllowDeleteOfParents,
37         $AllowInsert,
38         $AllowUpdate,
39         $SeparateDV,
40         $Permissions,
41         $AllowFilters,
42         $AllowSavingFilters,
43         $AllowSorting,
44         $AllowNavigation,
45         $AllowPrinting,
46         $HideTableView,
47         $AllowCSV,
48         $CSVSeparator,
49
50         $QuickSearch,
// 0 to 3
51
52         $RecordsPerPage,
53         $ScriptFileName,
54         $RedirectAfterInsert,
55         $TableTitle,
56         $PrimaryKey,
57         $DefaultSortField,
58         $DefaultSortDirection,
59
60         
// Templates variables
61         $Template,
62         $SelectedTemplate,
63         $TemplateDV,
64         $TemplateDVP,
65         $ShowTableHeader,
// 1 = show standard table headers
66         $TVClasses,
67         $DVClasses,
68         
// End of templates variables
69
70         $ContentType,
// set by DataList to 'tableview', 'detailview', 'tableview+detailview', 'print-tableview', 'print-detailview' or 'filters'
71         $HTML;
// generated html after calling Render()
72
73     function __construct(){
// PHP 7 compatibility
74         $
this->DataList();
75     }
76
77     function DataList(){
// Constructor function
78         $
this->DataHeight = 150;
79
80         $
this->AllowSelection = 1;
81         $
this->AllowDelete = 1;
82         $
this->AllowInsert = 1;
83         $
this->AllowUpdate = 1;
84         $
this->AllowFilters = 1;
85         $
this->AllowNavigation = 1;
86         $
this->AllowPrinting = 1;
87         $
this->HideTableView = 0;
88         $
this->QuickSearch = 0;
89         $
this->AllowCSV = 0;
90         $
this->CSVSeparator = ",";
91         $
this->HighlightColor = '#FFF0C2'; // default highlight color
92
93         $
this->RecordsPerPage = 10;
94         $
this->Template = '';
95         $
this->HTML = '';
96         $
this->filterers = array();
97     }
98
99     function showTV(){
100         
if($this->SeparateDV){
101             $
this->HideTableView = ($this->Permissions[2]==0 ? 1 : 0);
102         }
103     }
104
105     function hideTV(){
106         
if($this->SeparateDV){
107             $
this->HideTableView = 1;
108         }
109     }
110
111     function set_headers(){
112         @header(
'Content-Type: text/html; charset=' . datalist_db_encoding);
113         @header(
'X-Frame-Options: SAMEORIGIN'); // prevent iframing by other sites to prevent clickjacking
114     }
115
116     function Render(){
117     
// get post and get variables
118         
global $Translation;
119
120         $adminConfig = config(
'adminConfig');
121
122         $FiltersPerGroup =
4;
123         $buttonWholeWidth =
136;
124
125         $current_view =
''; /* TV, DV, TVDV, TVP, DVP, Filters */
126
127         $Embedded = intval($_REQUEST[
'Embedded']);
128         $AutoClose = intval($_REQUEST[
'AutoClose']);
129
130         $SortField = $_REQUEST[
"SortField"];
131         $SortDirection = $_REQUEST[
"SortDirection"];
132         $FirstRecord = $_REQUEST[
"FirstRecord"];
133         $ScrollUp_y = $_REQUEST[
"ScrollUp_y"];
134         $ScrollDn_y = $_REQUEST[
"ScrollDn_y"];
135         $Previous_x = $_REQUEST[
"Previous_x"];
136         $Next_x = $_REQUEST[
"Next_x"];
137         $Filter_x = $_REQUEST[
"Filter_x"];
138         $SaveFilter_x = $_REQUEST[
"SaveFilter_x"];
139         $NoFilter_x = $_REQUEST[
"NoFilter_x"];
140         $CancelFilter = $_REQUEST[
"CancelFilter"];
141         $ApplyFilter = $_REQUEST[
"ApplyFilter"];
142         $Search_x = $_REQUEST[
"Search_x"];
143         $SearchString = (get_magic_quotes_gpc() ? stripslashes($_REQUEST[
'SearchString']) : $_REQUEST['SearchString']);
144         $CSV_x = $_REQUEST[
"CSV_x"];
145         $Print_x = $_REQUEST[
'Print_x'];
146         $PrintTV = $_REQUEST[
'PrintTV'];
147         $PrintDV = $_REQUEST[
'PrintDV'];
148         $SelectedID = (get_magic_quotes_gpc() ? stripslashes($_REQUEST[
'SelectedID']) : $_REQUEST['SelectedID']);
149         $insert_x = $_REQUEST[
'insert_x'];
150         $update_x = $_REQUEST[
'update_x'];
151         $delete_x = $_REQUEST[
'delete_x'];
152         $SkipChecks = $_REQUEST[
'confirmed'];
153         $deselect_x = $_REQUEST[
'deselect_x'];
154         $addNew_x = $_REQUEST[
'addNew_x'];
155         $dvprint_x = $_REQUEST[
'dvprint_x'];
156         $DisplayRecords = (in_array($_REQUEST[
'DisplayRecords'], array('user', 'group')) ? $_REQUEST['DisplayRecords'] : 'all');
157         list($FilterAnd, $FilterField, $FilterOperator, $FilterValue) = $
this->validate_filters($_REQUEST, $FiltersPerGroup);
158         $record_selector = array();
159         
if(isset($_REQUEST['record_selector']) && is_array($_REQUEST['record_selector']))
160             $record_selector = $_REQUEST[
'record_selector'];
161
162
163         $mi = getMemberInfo();
164
165     
// validate user inputs
166         
if(!preg_match('/^\s*[1-9][0-9]*\s*(asc|desc)?(\s*,\s*[1-9][0-9]*\s*(asc|desc)?)*$/i', $SortField)){
167             $SortField =
'';
168         }
169         
if(!preg_match('/^(asc|desc)$/i', $SortDirection)){
170             $SortDirection =
'';
171         }
172
173         
if(!$this->AllowDelete){
174             $delete_x =
'';
175         }
176         
if(!$this->AllowDeleteOfParents){
177             $SkipChecks =
'';
178         }
179         
if(!$this->AllowInsert){
180             $insert_x =
'';
181             $addNew_x =
'';
182         }
183         
if(!$this->AllowUpdate){
184             $update_x =
'';
185         }
186         
if(!$this->AllowFilters){
187             $Filter_x =
'';
188         }
189         
if(!$this->AllowPrinting){
190             $Print_x =
'';
191             $PrintTV =
'';
192         }
193         
if(!$this->QuickSearch){
194             $SearchString =
'';
195         }
196         
if(!$this->AllowCSV){
197             $CSV_x =
'';
198         }
199
200     
// enforce record selection if user has edit/delete permissions on the current table
201         $AllowPrintDV=
1;
202         $
this->Permissions=getTablePermissions($this->TableName);
203         
if($this->Permissions[3] || $this->Permissions[4]){ // current user can edit or delete?
204             $
this->AllowSelection = 1;
205         }elseif(!$
this->AllowSelection){
206             $SelectedID=
'';
207             $AllowPrintDV=
0;
208             $PrintDV=
'';
209         }
210
211         
if(!$this->AllowSelection || !$SelectedID){ $dvprint_x=''; }
212
213         $
this->QueryFieldsIndexed=reIndex($this->QueryFieldsFilters);
214
215     
// determine type of current view: TV, DV, TVDV, TVP, DVP or Filters?
216         
if($this->SeparateDV){
217             $current_view =
'TV';
218             
if($Print_x != '' || $PrintTV != '') $current_view = 'TVP';
219             elseif($dvprint_x !=
'' || $PrintDV != '') $current_view = 'DVP';
220             elseif($Filter_x !=
'') $current_view = 'Filters';
221             elseif(($SelectedID && !$deselect_x && !$delete_x) || $addNew_x !=
'') $current_view = 'DV';
222         }
else{
223             $current_view =
'TVDV';
224             
if($Print_x != '' || $PrintTV != '') $current_view = 'TVP';
225             elseif($dvprint_x !=
'' || $PrintDV != '') $current_view = 'DVP';
226             elseif($Filter_x !=
'') $current_view = 'Filters';
227         }
228
229         $
this->HTML .= '<div class="row"><div class="col-xs-12">';
230         $
this->HTML .= '<form ' . (datalist_image_uploads_exist ? 'enctype="multipart/form-data" ' : '') . 'method="post" name="myform" action="' . $this->ScriptFileName . '">';
231         
if($Embedded) $this->HTML .= '<input name="Embedded" value="1" type="hidden">';
232         
if($AutoClose) $this->HTML .= '<input name="AutoClose" value="1" type="hidden">';
233         $
this->HTML .= '<script>';
234         $
this->HTML .= 'function enterAction(){';
235         $
this->HTML .= ' if($j("input[name=SearchString]:focus").length){ $j("#Search").click(); }';
236         $
this->HTML .= ' return false;';
237         $
this->HTML .= '}';
238         $
this->HTML .= '</script>';
239         $
this->HTML .= '<input id="EnterAction" type="submit" style="position: absolute; left: 0px; top: -250px;" onclick="return enterAction();">';
240
241         $
this->ContentType='tableview'; // default content type
242
243         
if($PrintTV != ''){
244             $Print_x =
1;
245             $_REQUEST[
'Print_x'] = 1;
246         }
247
248     
// handle user commands ...
249         
if($deselect_x != ''){
250             $SelectedID =
'';
251             $
this->showTV();
252         }
253
254         elseif($insert_x !=
''){
255             $SelectedID = call_user_func($
this->TableName.'_insert');
256
257             
// redirect to a safe url to avoid refreshing and thus
258             
// insertion of duplicate records.
259             $url = $
this->RedirectAfterInsert;
260             $insert_status =
'record-added-ok=' . rand();
261             
if(!$SelectedID) $insert_status = 'record-added-error=' . rand();
262
263             
// compose filters and sorting
264             
foreach($this->filterers as $filterer => $caption){
265                 
if($_REQUEST['filterer_' . $filterer] != '') $filtersGET .= '&filterer_' . $filterer . '=' . urlencode($_REQUEST['filterer_' . $filterer]);
266             }
267             
for($i = 1; $i <= (datalist_filters_count * $FiltersPerGroup); $i++){ // Number of filters allowed
268                 
if($FilterField[$i] != '' && $FilterOperator[$i] != '' && ($FilterValue[$i] != '' || strpos($FilterOperator[$i], 'empty'))){
269                     $filtersGET .=
"&FilterAnd[{$i}]={$FilterAnd[$i]}&FilterField[{$i}]={$FilterField[$i]}&FilterOperator[{$i}]={$FilterOperator[$i]}&FilterValue[{$i}]=" . urlencode($FilterValue[$i]);
270                 }
271             }
272             
if($Embedded) $filtersGET .= '&Embedded=1&SelectedID=' . urlencode($SelectedID);
273             
if($AutoClose) $filtersGET .= '&AutoClose=1';
274             $filtersGET .=
"&SortField={$SortField}&SortDirection={$SortDirection}&FirstRecord={$FirstRecord}";
275             $filtersGET .=
"&DisplayRecords={$DisplayRecords}";
276             $filtersGET .=
'&SearchString=' . urlencode($SearchString);
277             $filtersGET = substr($filtersGET,
1); // remove initial &
278
279             
if($url){
280                 
/* if designer specified a redirect-after-insert url */
281                 $url .= (strpos($url,
'?') !== false ? '&' : '?') . $insert_status;
282                 $url .= (strpos($url, $
this->ScriptFileName) !== false ? "&{$filtersGET}" : '');
283                 $url = str_replace(
"#ID#", urlencode($SelectedID), $url);
284             }
else{
285                 
/* if no redirect-after-insert url, use default */
286                 $url =
"{$this->ScriptFileName}?{$insert_status}&{$filtersGET}";
287
288                 
/* if DV and TV in same page, select new record */
289                 
if(!$this->SeparateDV) $url .= '&SelectedID=' . urlencode($SelectedID);
290             }
291
292             @header(
'Location: ' . $url);
293             $
this->HTML .= "<META HTTP-EQUIV=\"Refresh\" CONTENT=\"0;url=" . $url ."\">";
294
295             
return;
296         }
297
298         elseif($delete_x !=
''){
299             $delete_res = call_user_func($
this->TableName.'_delete', $SelectedID, $this->AllowDeleteOfParents, $SkipChecks);
300             
// handle ajax delete requests
301             
if(is_ajax()){
302                 die($delete_res ? $delete_res :
'OK');
303             }
304
305             
if($delete_res){
306                 
//$_REQUEST['record-deleted-error'] = 1;
307                 $
this->HTML .= showNotifications($delete_res, 'alert alert-danger', false);
308                 $
this->hideTV();
309                 $current_view = ($
this->SeparateDV ? 'DV' : 'TVDV');
310             }
else{
311                 $_REQUEST[
'record-deleted-ok'] = 1;
312                 $SelectedID =
'';
313                 $
this->showTV();
314
315                 
/* close window if embedded */
316                 
if($Embedded){
317                     $
this->HTML .= '<script>$j(function(){ setTimeout(function(){ AppGini.closeParentModal(); }, 2000); })</script>';
318                 }
319             }
320         }
321
322         elseif($update_x !=
''){
323             $updated = call_user_func($
this->TableName.'_update', $SelectedID);
324
325             $update_status =
'record-updated-ok=' . rand();
326             
if($updated === false) $update_status = 'record-updated-error=' . rand();
327
328             
// compose filters and sorting
329             
foreach($this->filterers as $filterer => $caption){
330                 
if($_REQUEST['filterer_' . $filterer] != '') $filtersGET .= '&filterer_' . $filterer . '=' . urlencode($_REQUEST['filterer_' . $filterer]);
331             }
332             
for($i = 1; $i <= (datalist_filters_count * $FiltersPerGroup); $i++){ // Number of filters allowed
333                 
if($FilterField[$i] != '' && $FilterOperator[$i] != '' && ($FilterValue[$i] != '' || strpos($FilterOperator[$i], 'empty'))){
334                     $filtersGET .=
"&FilterAnd[{$i}]={$FilterAnd[$i]}&FilterField[{$i}]={$FilterField[$i]}&FilterOperator[{$i}]={$FilterOperator[$i]}&FilterValue[{$i}]=" . urlencode($FilterValue[$i]);
335                 }
336             }
337             $filtersGET .=
"&SortField={$SortField}&SortDirection={$SortDirection}&FirstRecord={$FirstRecord}&Embedded={$Embedded}";
338             
if($AutoClose) $filtersGET .= '&AutoClose=1';
339             $filtersGET .=
"&DisplayRecords={$DisplayRecords}";
340             $filtersGET .=
'&SearchString=' . urlencode($SearchString);
341             $filtersGET = substr($filtersGET,
1); // remove initial &
342
343             $redirectUrl = $
this->ScriptFileName . '?SelectedID=' . urlencode($SelectedID) . '&' . $filtersGET . '&' . $update_status;
344             @header(
"Location: $redirectUrl");
345             $
this->HTML .= '<META HTTP-EQUIV="Refresh" CONTENT="0;url='.$redirectUrl.'">';
346             
return;
347         }
348
349         elseif($addNew_x !=
''){
350             $SelectedID=
'';
351             $
this->hideTV();
352         }
353
354         elseif($Print_x !=
''){
355             
// print code here ....
356             $
this->AllowNavigation = 0;
357             $
this->AllowSelection = 0;
358         }
359
360         elseif($SaveFilter_x !=
'' && $this->AllowSavingFilters){
361             $filter_link = $_SERVER[
'HTTP_REFERER'] . '?SortField=' . urlencode($SortField) . '&SortDirection=' . $SortDirection . '&';
362             
for($i = 1; $i <= (datalist_filters_count * $FiltersPerGroup); $i++){ // Number of filters allowed
363                 
if(($FilterField[$i] != '' || $i == 1) && $FilterOperator[$i] != '' && ($FilterValue[$i] != '' || strpos($FilterOperator[$i], 'empty'))){
364                     $filter_link .= urlencode(
"FilterAnd[$i]") . '=' . urlencode($FilterAnd[$i]) . '&';
365                     $filter_link .= urlencode(
"FilterField[$i]") . '=' . urlencode($FilterField[$i]) . '&';
366                     $filter_link .= urlencode(
"FilterOperator[$i]") . '=' . urlencode($FilterOperator[$i]) . '&';
367                     $filter_link .= urlencode(
"FilterValue[$i]") . '=' . urlencode($FilterValue[$i]) . '&';
368                 }elseif(($i % $FiltersPerGroup ==
1) && in_array($FilterAnd[$i], array('and', 'or'))){
369                     
/* always include the and/or at the beginning of each group */
370                     $filter_link .= urlencode(
"FilterAnd[$i]") . '=' . urlencode($FilterAnd[$i]) . '&';
371                 }
372             }
373             $filter_link = substr($filter_link,
0, -1); /* trim last '&' */
374
375             $
this->HTML .= '<div id="saved_filter_source_code" class="row"><div class="col-md-6 col-md-offset-3">';
376                 $
this->HTML .= '<div class="panel panel-info">';
377                     $
this->HTML .= '<div class="panel-heading"><h3 class="panel-title">' . $Translation["saved filters title"] . "</h3></div>";
378                     $
this->HTML .= '<div class="panel-body">';
379                         $
this->HTML .= $Translation["saved filters instructions"];
380                         $
this->HTML .= '<textarea rows="4" class="form-control vspacer-lg" style="width: 100%;" onfocus="$j(this).select();">' . "&lt;a href=\"{$filter_link}\"&gt;Saved filter link&lt;a&gt;" . '</textarea>';
381                         $
this->HTML .= "<div><a href=\"{$filter_link}\" title=\"" . html_attr($filter_link) . "\">{$Translation['permalink']}</a></div>";
382                         $
this->HTML .= '<button type="button" class="btn btn-default btn-block vspacer-lg" onclick="$j(\'#saved_filter_source_code\').remove();"><i class="glyphicon glyphicon-remove"></i> ' . $Translation['hide code'] . '</button>';
383                     $
this->HTML .= '</div>';
384                 $
this->HTML .= '</div>';
385             $
this->HTML .= '</div></div>';
386         }
387
388         elseif($Filter_x !=
''){
389             $orderBy = array();
390             
if($SortField){
391                 $sortFields = explode(
',', $SortField);
392                 $i=
0;
393                 
foreach($sortFields as $sf){
394                     $tob = preg_split(
'/\s+/', $sf, 2);
395                     $orderBy[] = array(trim($tob[
0]) => (strtolower(trim($tob[1]))=='desc' ? 'desc' : 'asc'));
396                     $i++;
397                 }
398                 $orderBy[$i-
1][$tob[0]] = (strtolower(trim($SortDirection))=='desc' ? 'desc' : 'asc');
399             }
400
401             $currDir=dirname(__FILE__).
'/hooks'; // path to hooks folder
402             $uff=
"{$currDir}/{$this->TableName}.filters.{$mi['username']}.php"; // user-specific filter file
403             $gff=
"{$currDir}/{$this->TableName}.filters.{$mi['group']}.php"; // group-specific filter file
404             $tff=
"{$currDir}/{$this->TableName}.filters.php"; // table-specific filter file
405
406             
/*
407                 
if no explicit filter file exists, look for filter files in the hooks folder in this order:
408                     
1. tablename.filters.username.php ($uff)
409                     
2. tablename.filters.groupname.php ($gff)
410                     
3. tablename.filters.php ($tff)
411             */

412             
if(!is_file($this->FilterPage)){
413                 $
this->FilterPage='defaultFilters.php';
414                 
if(is_file($uff)){
415                     $
this->FilterPage=$uff;
416                 }elseif(is_file($gff)){
417                     $
this->FilterPage=$gff;
418                 }elseif(is_file($tff)){
419                     $
this->FilterPage=$tff;
420                 }
421             }
422
423             
if($this->FilterPage!=''){
424                 ob_start();
425                 @include($
this->FilterPage);
426                 $
out=ob_get_contents();
427                 ob_end_clean();
428                 $
this->HTML .= $out;
429             }
430             
// hidden variables ....
431                 $
this->HTML .= '<input name="SortField" value="'.$SortField.'" type="hidden" />';
432                 $
this->HTML .= '<input name="SortDirection" type="hidden" value="'.$SortDirection.'" />';
433                 $
this->HTML .= '<input name="FirstRecord" type="hidden" value="1" />';
434
435                 $
this->ContentType='filters';
436             $
this->set_headers();
437             
return;
438         }
439
440         elseif($NoFilter_x !=
''){
441             
// clear all filters ...
442             
for($i = 1; $i <= (datalist_filters_count * $FiltersPerGroup); $i++){ // Number of filters allowed
443                 $FilterField[$i] =
'';
444                 $FilterOperator[$i] =
'';
445                 $FilterValue[$i] =
'';
446             }
447             $DisplayRecords =
'all';
448             $SearchString =
'';
449             $FirstRecord =
1;
450
451             
// clear filterers
452             
foreach($this->filterers as $filterer => $caption){
453                 $_REQUEST[
'filterer_' . $filterer] = '';
454             }
455         }
456
457         elseif($SelectedID){
458             $
this->hideTV();
459         }
460
461     
// apply lookup filterers to the query
462         
foreach($this->filterers as $filterer => $caption){
463             
if($_REQUEST['filterer_' . $filterer] != ''){
464                 
if($this->QueryWhere == '')
465                     $
this->QueryWhere = "where ";
466                 
else
467                     $
this->QueryWhere .= " and ";
468                 $
this->QueryWhere .= "`{$this->TableName}`.`$filterer`='" . makeSafe($_REQUEST['filterer_' . $filterer]) . "' ";
469                 
break; // currently, only one filterer can be applied at a time
470             }
471         }
472
473     
// apply quick search to the query
474         
if($SearchString != ''){
475             
if($Search_x!=''){ $FirstRecord=1; }
476
477             
if($this->QueryWhere=='')
478                 $
this->QueryWhere = "where ";
479             
else
480                 $
this->QueryWhere .= " and ";
481
482             
foreach($this->QueryFieldsQS as $fName => $fCaption){
483                 
if(strpos($fName, '<img')===False){
484                     $
this->QuerySearchableFields[$fName]=$fCaption;
485                 }
486             }
487
488             $
this->QueryWhere.='('.implode(" LIKE '%".makeSafe($SearchString)."%' or ", array_keys($this->QuerySearchableFields))." LIKE '%".makeSafe($SearchString)."%')";
489         }
490
491
492     
// set query filters
493         $QueryHasWhere =
0;
494         
if(strpos($this->QueryWhere, 'where ')!==FALSE)
495             $QueryHasWhere =
1;
496
497         $WhereNeedsClosing =
0;
498         
for($i = 1; $i <= (datalist_filters_count * $FiltersPerGroup); $i+=$FiltersPerGroup){ // Number of filters allowed
499             
// test current filter group
500             $GroupHasFilters =
0;
501             
for($j = 0; $j < $FiltersPerGroup; $j++){
502                 
if($FilterField[$i+$j] != '' && $this->QueryFieldsIndexed[($FilterField[$i+$j])] != '' && $FilterOperator[$i+$j] != '' && ($FilterValue[$i+$j] != '' || strpos($FilterOperator[$i+$j], 'empty'))){
503                     $GroupHasFilters =
1;
504                     
break;
505                 }
506             }
507
508             
if($GroupHasFilters){
509                 
if(!stristr($this->QueryWhere, "where "))
510                     $
this->QueryWhere = "where (";
511                 elseif($QueryHasWhere){
512                     $
this->QueryWhere .= " and (";
513                     $QueryHasWhere =
0;
514                 }
515
516                 $
this->QueryWhere .= " <FilterGroup> " . $FilterAnd[$i] . " (";
517
518                 
for($j = 0; $j < $FiltersPerGroup; $j++){
519                     
if($FilterField[$i+$j] != '' && $this->QueryFieldsIndexed[($FilterField[$i+$j])] != '' && $FilterOperator[$i+$j] != '' && ($FilterValue[$i+$j] != '' || strpos($FilterOperator[$i+$j], 'empty'))){
520                         
if($FilterAnd[$i+$j]==''){
521                             $FilterAnd[$i+$j]=
'and';
522                         }
523                         
// test for date/time fields
524                         $tries =
0; $isDateTime = $isDate = false;
525                         $fieldName=str_replace(
'`', '', $this->QueryFieldsIndexed[($FilterField[$i+$j])]);
526                         list($tn, $fn)=explode(
'.', $fieldName);
527                         
while(!($res = sql("show columns from `{$tn}` like '{$fn}'", $eo)) && $tries < 2){
528                             $tn=substr($tn,
0, -1);
529                             $tries++;
530                         }
531                         
if($row = @db_fetch_array($res)){
532                             $isDateTime = in_array($row[
'Type'], array('date', 'time', 'datetime'));
533                             $isDate = in_array($row[
'Type'], array('date', 'datetime'));
534                         }
535                         
// end of test
536                         
if($FilterOperator[$i+$j]=='is-empty' && !$isDateTime){
537                             $
this->QueryWhere .= " <FilterItem> " . $FilterAnd[$i+$j] . " (" . $this->QueryFieldsIndexed[($FilterField[$i+$j])] . "='' or " . $this->QueryFieldsIndexed[($FilterField[$i+$j])] . " is NULL) </FilterItem>";
538                         }elseif($FilterOperator[$i+$j]==
'is-not-empty' && !$isDateTime){
539                             $
this->QueryWhere .= " <FilterItem> " . $FilterAnd[$i+$j] . " " . $this->QueryFieldsIndexed[($FilterField[$i+$j])] . "!='' </FilterItem>";
540                         }elseif($FilterOperator[$i+$j]==
'is-empty' && $isDateTime){
541                             $
this->QueryWhere .= " <FilterItem> " . $FilterAnd[$i+$j] . " (" . $this->QueryFieldsIndexed[($FilterField[$i+$j])] . "=0 or " . $this->QueryFieldsIndexed[($FilterField[$i+$j])] . " is NULL) </FilterItem>";
542                         }elseif($FilterOperator[$i+$j]==
'is-not-empty' && $isDateTime){
543                             $
this->QueryWhere .= " <FilterItem> " . $FilterAnd[$i+$j] . " " . $this->QueryFieldsIndexed[($FilterField[$i+$j])] . "!=0 </FilterItem>";
544                         }elseif($FilterOperator[$i+$j]==
'like' && !strstr($FilterValue[$i+$j], "%") && !strstr($FilterValue[$i+$j], "_")){
545                             $
this->QueryWhere .= " <FilterItem> " . $FilterAnd[$i+$j] . " " . $this->QueryFieldsIndexed[($FilterField[$i+$j])] . " like '%" . makeSafe($FilterValue[$i+$j]) . "%' </FilterItem>";
546                         }elseif($FilterOperator[$i+$j]==
'not-like' && !strstr($FilterValue[$i+$j], "%") && !strstr($FilterValue[$i+$j], "_")){
547                             $
this->QueryWhere .= " <FilterItem> " . $FilterAnd[$i+$j] . " " . $this->QueryFieldsIndexed[($FilterField[$i+$j])] . " not like '%" . makeSafe($FilterValue[$i+$j]) . "%' </FilterItem>";
548                         }elseif($isDate){
549                             $dateValue = mysql_datetime($FilterValue[$i + $j]);
550                             $
this->QueryWhere .= " <FilterItem> " . $FilterAnd[$i+$j] . " " . $this->QueryFieldsIndexed[($FilterField[$i+$j])] . " " . $GLOBALS['filter_operators'][$FilterOperator[$i+$j]] . " '$dateValue' </FilterItem>";
551                         }
else{
552                             $
this->QueryWhere .= " <FilterItem> " . $FilterAnd[$i+$j] . " " . $this->QueryFieldsIndexed[($FilterField[$i+$j])] . " " . $GLOBALS['filter_operators'][$FilterOperator[$i+$j]] . " '" . makeSafe($FilterValue[$i+$j]) . "' </FilterItem>";
553                         }
554                     }
555                 }
556
557                 $
this->QueryWhere .= ") </FilterGroup>";
558                 $WhereNeedsClosing =
1;
559             }
560         }
561
562         
if($WhereNeedsClosing)
563             $
this->QueryWhere .= ")";
564
565     
// set query sort
566         
if(!stristr($this->QueryOrder, "order by ") && $SortField != '' && $this->AllowSorting){
567             $actualSortField = $SortField;
568             
foreach($this->SortFields as $fieldNum => $fieldSort){
569                 $actualSortField = str_replace(
" $fieldNum ", " $fieldSort ", " $actualSortField ");
570                 $actualSortField = str_replace(
",$fieldNum ", ",$fieldSort ", " $actualSortField ");
571             }
572             $
this->QueryOrder = "order by $actualSortField $SortDirection";
573         }
574
575     
// clean up query
576         $
this->QueryWhere = str_replace('( <FilterGroup> and ', '( ', $this->QueryWhere);
577         $
this->QueryWhere = str_replace('( <FilterGroup> or ', '( ', $this->QueryWhere);
578         $
this->QueryWhere = str_replace('( <FilterItem> and ', '( ', $this->QueryWhere);
579         $
this->QueryWhere = str_replace('( <FilterItem> or ', '( ', $this->QueryWhere);
580         $
this->QueryWhere = str_replace('<FilterGroup>', '', $this->QueryWhere);
581         $
this->QueryWhere = str_replace('</FilterGroup>', '', $this->QueryWhere);
582         $
this->QueryWhere = str_replace('<FilterItem>', '', $this->QueryWhere);
583         $
this->QueryWhere = str_replace('</FilterItem>', '', $this->QueryWhere);
584
585     
// if no 'order by' clause found, apply default sorting if specified
586         
if($this->DefaultSortField != '' && $this->QueryOrder == ''){
587             $
this->QueryOrder="order by ".$this->DefaultSortField." ".$this->DefaultSortDirection;
588         }
589
590     
// get count of matching records ...
591         $TempQuery =
'SELECT count(1) from '.$this->QueryFrom.' '.$this->QueryWhere;
592         $RecordCount = sqlValue($TempQuery);
593         $FieldCountTV = count($
this->QueryFieldsTV);
594         $FieldCountCSV = count($
this->QueryFieldsCSV);
595         $FieldCountFilters = count($
this->QueryFieldsFilters);
596         
if(!$RecordCount){
597             $FirstRecord=
1;
598         }
599
600     
// Output CSV on request
601         
if($CSV_x != ''){
602             $
this->HTML = '';
603             
if(datalist_db_encoding == 'UTF-8') $this->HTML = "\xEF\xBB\xBF"; // BOM characters for UTF-8 output
604
605         
// execute query for CSV output
606             $fieldList=
'';
607             
foreach($this->QueryFieldsCSV as $fn=>$fc)
608                 $fieldList.=
"$fn as `$fc`, ";
609             $fieldList=substr($fieldList,
0, -2);
610             $csvQuery =
'SELECT '.$fieldList.' from '.$this->QueryFrom.' '.$this->QueryWhere.' '.$this->QueryOrder;
611
612             
// hook: table_csv
613             
if(function_exists($this->TableName.'_csv')){
614                 $args = array();
615                 $mq = call_user_func_array($
this->TableName . '_csv', array($csvQuery, $mi, &$args));
616                 $csvQuery = ($mq ? $mq : $csvQuery);
617             }
618
619             $result = sql($csvQuery, $eo);
620
621         
// output CSV field names
622             
for($i = 0; $i < $FieldCountCSV; $i++)
623                 $
this->HTML .= "\"" . db_field_name($result, $i) . "\"" . $this->CSVSeparator;
624             $
this->HTML .= "\n\n";
625
626         
// output CSV data
627             
while($row = db_fetch_row($result)){
628                 
for($i = 0; $i < $FieldCountCSV; $i++)
629                     $
this->HTML .= "\"" . str_replace(array("\r\n", "\r", "\n", '"'), array(' ', ' ', ' ', '""'), strip_tags($row[$i])) . "\"" . $this->CSVSeparator;
630                 $
this->HTML .= "\n\n";
631             }
632             $
this->HTML = str_replace($this->CSVSeparator . "\n\n", "\n", $this->HTML);
633             $
this->HTML = substr($this->HTML, 0, - 1);
634
635         
// clean any output buffers
636             
while(@ob_end_clean());
637
638         
// output CSV HTTP headers ...
639             header(
'HTTP/1.1 200 OK');
640             header(
'Date: ' . @date("D M j G:i:s T Y"));
641             header(
'Last-Modified: ' . @date("D M j G:i:s T Y"));
642             header(
"Content-Type: application/force-download");
643             header(
"Content-Length: " . (string)(strlen($this->HTML)));
644             header(
"Content-Transfer-Encoding: Binary");
645             header(
"Content-Disposition: attachment; filename=$this->TableName.csv");
646
647         
// send output and quit script
648             echo $
this->HTML;
649             exit;
650         }
651         $t = time();
// just a random number for any purpose ...
652
653     
// should SelectedID be reset on clicking TV buttons?
654         $resetSelection = ($
this->SeparateDV ? "document.myform.SelectedID.value = '';" : "document.myform.writeAttribute('novalidate', 'novalidate');");
655
656         
if($current_view == 'DV' && !$Embedded){
657             $
this->HTML .= '<div class="page-header">';
658                 $
this->HTML .= '<h1>';
659                     $
this->HTML .= '<a style="text-decoration: none; color: inherit;" href="' . $this->TableName . '_view.php"><img src="' . $this->TableIcon . '"> ' . $this->TableTitle . '</a>';
660                     
/* show add new button if user can insert and there is a selected record */
661                     
if($SelectedID && $this->Permissions[1] && $this->SeparateDV && $this->AllowInsert){
662                         $
this->HTML .= ' <button type="submit" id="addNew" name="addNew_x" value="1" class="btn btn-success"><i class="glyphicon glyphicon-plus-sign"></i> ' . $Translation['Add New'] . '</button>';
663                     }
664                 $
this->HTML .= '</h1>';
665             $
this->HTML .= '</div>';
666         }
667
668     
// quick search and TV action buttons
669         
if(!$this->HideTableView && !($dvprint_x && $this->AllowSelection && $SelectedID) && !$PrintDV){
670             $buttons_all = $quick_search_html =
'';
671
672             
if($Print_x == ''){
673
674                 
// display 'Add New' icon
675                 
if($this->Permissions[1] && $this->SeparateDV && $this->AllowInsert){
676                     $buttons_all .=
'<button type="submit" id="addNew" name="addNew_x" value="1" class="btn btn-success"><i class="glyphicon glyphicon-plus-sign"></i> ' . $Translation['Add New'] . '</button>';
677                     $buttonsCount++;
678                 }
679
680                 
// display Print icon
681                 
if($this->AllowPrinting){
682                     $buttons_all .=
'<button onClick="document.myform.NoDV.value=1; ' . $resetSelection . ' return true;" type="submit" name="Print_x" id="Print" value="1" class="btn btn-default"><i class="glyphicon glyphicon-print"></i> ' . $Translation['Print Preview'] . '</button>';
683                     $buttonsCount++;
684                 }
685
686                 
// display CSV icon
687                 
if($this->AllowCSV){
688                     $buttons_all .=
'<button onClick="document.myform.NoDV.value=1; ' . $resetSelection . ' return true;" type="submit" name="CSV_x" id="CSV" value="1" class="btn btn-default"><i class="glyphicon glyphicon-download-alt"></i> ' . $Translation['CSV'] . '</button>';
689                     $buttonsCount++;
690                 }
691
692                 
// display Filter icon
693                 
if($this->AllowFilters){
694                     $buttons_all .=
'<button onClick="document.myform.NoDV.value=1; ' . $resetSelection . ' return true;" type="submit" name="Filter_x" id="Filter" value="1" class="btn btn-default"><i class="glyphicon glyphicon-filter"></i> ' . $Translation['filter'] . '</button>';
695                     $buttonsCount++;
696                 }
697
698                 
// display Show All icon
699                 
if(($this->AllowFilters)){
700                     $buttons_all .=
'<button onClick="document.myform.NoDV.value=1; ' . $resetSelection . ' return true;" type="submit" name="NoFilter_x" id="NoFilter" value="1" class="btn btn-default"><i class="glyphicon glyphicon-remove-circle"></i> ' . $Translation['Reset Filters'] . '</button>';
701                     $buttonsCount++;
702                 }
703
704                 $quick_search_html .= quick_search_html($SearchString, $
this->QuickSearchText, $this->SeparateDV);
705             }
else{
706                 $buttons_all .=
'<button class="btn btn-primary" type="button" id="sendToPrinter" onClick="window.print();"><i class="glyphicon glyphicon-print"></i> ' . $Translation['Print'] . '</button>';
707                 $buttons_all .=
'<button class="btn btn-default" type="submit"><i class="glyphicon glyphicon-remove-circle"></i> ' . $Translation['Cancel Printing'] . '</button>';
708             }
709
710             
/* if user can print DV, add action to 'More' menu */
711             $selected_records_more = array();
712
713             
if($AllowPrintDV){
714                 $selected_records_more[] = array(
715                     
'function' => ($this->SeparateDV ? 'print_multiple_dv_sdv' : 'print_multiple_dv_tvdv'),
716                     
'title' => $Translation['Print Preview Detail View'],
717                     
'icon' => 'print'
718                 );
719             }
720
721             
/* if user can mass-delete selected records, add action to 'More' menu */
722             
if($this->AllowMassDelete && $this->AllowDelete){
723                 $selected_records_more[] = array(
724                     
'function' => 'mass_delete',
725                     
'title' => $Translation['Delete'],
726                     
'icon' => 'trash',
727                     
'class' => 'text-danger'
728                 );
729             }
730
731             
/* if user is admin, add 'Change owner' action to 'More' menu */
732             
/* also, add help link for adding more actions */
733             
if($mi['admin']){
734                 $selected_records_more[] = array(
735                     
'function' => 'mass_change_owner',
736                     
'title' => $Translation['Change owner'],
737                     
'icon' => 'user'
738                 );
739                 $selected_records_more[] = array(
740                     
'function' => 'add_more_actions_link',
741                     
'title' => $Translation['Add more actions'],
742                     
'icon' => 'question-sign',
743                     
'class' => 'text-info'
744                 );
745             }
746
747             
/* user-defined actions ... should be set in the {tablename}_batch_actions() function in hooks/{tablename}.php */
748             $user_actions = array();
749             
if(function_exists($this->TableName.'_batch_actions')){
750                 $args = array();
751                 $user_actions = call_user_func_array($
this->TableName . '_batch_actions', array(&$args));
752                 
if(is_array($user_actions) && count($user_actions)){
753                     $selected_records_more = array_merge($selected_records_more, $user_actions);
754                 }
755             }
756
757             $actual_more_count =
0;
758             $more_menu = $more_menu_js =
'';
759             
if(count($selected_records_more)){
760                 $more_menu .=
'<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" id="selected_records_more"><i class="glyphicon glyphicon-check"></i> ' . $Translation['More'] . ' <span class="caret"></span></button>';
761                 $more_menu .=
'<ul class="dropdown-menu" role="menu">';
762                 
foreach($selected_records_more as $action){
763                     
if(!$action['function'] || !$action['title']) continue;
764                     $action[
'class'] = (!isset($action['class']) ? '' : $action['class']);
765                     $action[
'icon'] = (!isset($action['icon']) ? '' : $action['icon']);
766                     $actual_more_count++;
767                     $more_menu .=
'<li>' .
768                             
'<a href="#" id="selected_records_' . $action['function'] . '">' .
769                                 
'<span class="' . $action['class'] . '">' .
770                                     ($action[
'icon'] ? '<i class="glyphicon glyphicon-' . $action['icon'] . '"></i> ' : '') .
771                                     $action[
'title'] .
772                                 
'</span>' .
773                             
'</a>' .
774                         
'</li>';
775
776                     
// on clicking an action, call its js handler function, passing the current table name and an array of selected IDs to it
777                     $more_menu_js .=
"jQuery('[id=selected_records_{$action['function']}]').click(function(){ {$action['function']}('{$this->TableName}', get_selected_records_ids()); return false; });";
778                 }
779                 $more_menu .=
'</ul>';
780             }
781
782             
if($Embedded){
783                 $
this->HTML .= '<script>$j(function(){ $j(\'[id^=notification-]\').parent().css({\'margin-top\': \'15px\', \'margin-bottom\': \'0\'}); })</script>';
784             }
else{
785                 $
this->HTML .= '<div class="page-header">';
786                     $
this->HTML .= '<h1>';
787                         $
this->HTML .= '<div class="row">';
788                             $
this->HTML .= '<div class="col-sm-8">';
789                                 $
this->HTML .= '<a style="text-decoration: none; color: inherit;" href="' . $this->TableName . '_view.php"><img src="' . $this->TableIcon . '"> ' . $this->TableTitle . '</a>';
790                                 
/* show add new button if user can insert and there is a selected record */
791                                 
if($SelectedID && $this->Permissions[1] && !$this->SeparateDV && $this->AllowInsert){
792                                     $
this->HTML .= ' <button type="submit" id="addNew" name="addNew_x" value="1" class="btn btn-success"><i class="glyphicon glyphicon-plus-sign"></i> ' . $Translation['Add New'] . '</button>';
793                                 }
794                             $
this->HTML .= '</div>';
795                             
if($this->QuickSearch){
796                                 $
this->HTML .= '<div class="col-sm-4">';
797                                     $
this->HTML .= $quick_search_html;
798                                 $
this->HTML .= '</div>';
799                             }
800                         $
this->HTML .= '</div>';
801                     $
this->HTML .= '</h1>';
802                 $
this->HTML .= '</div>';
803
804                 $
this->HTML .= '<div id="top_buttons" class="hidden-print">';
805                     
/* .all_records: container for buttons that don't need a selection */
806                     
/* .selected_records: container for buttons that need a selection */
807                     $
this->HTML .= '<div class="btn-group btn-group-lg visible-md visible-lg all_records pull-left">' . $buttons_all . '</div>';
808                     $
this->HTML .= '<div class="btn-group btn-group-lg visible-md visible-lg selected_records hidden pull-left hspacer-lg">' . ($actual_more_count ? $more_menu : '') . '</div>';
809                     $
this->HTML .= '<div class="btn-group-vertical btn-group-lg visible-xs visible-sm all_records">' . $buttons_all . '</div>';
810                     $
this->HTML .= '<div class="btn-group-vertical btn-group-lg visible-xs visible-sm selected_records hidden vspacer-lg">' . ($actual_more_count ? $more_menu : '') . '</div>';
811                     $
this->HTML .= $this->tv_tools();
812                     $
this->HTML .= '<p></p>';
813                 $
this->HTML .= '</div>';
814
815                 $
this->HTML .= '<div class="row"><div class="table_view col-xs-12 ' . $this->TVClasses . '">';
816             }
817
818             
if($Print_x != ''){
819                 
/* fix top margin for print-preview */
820                 $
this->HTML .= '<style>body{ padding-top: 0 !important; }</style>';
821
822                 
/* disable links inside table body to prevent printing their href */
823                 $
this->HTML .= '<script>jQuery(function(){ jQuery("tbody a").removeAttr("href").removeAttr("rel"); });</script>';
824             }
825
826             
// script for focusing into the search box on loading the page
827             
// and for declaring record action handlers
828             $
this->HTML .= '<script>jQuery(function(){ jQuery("input[name=SearchString]").focus(); ' . $more_menu_js . ' });</script>';
829
830         }
831
832     
// begin table and display table title
833         
if(!$this->HideTableView && !($dvprint_x && $this->AllowSelection && $SelectedID) && !$PrintDV && !$Embedded){
834             $
this->HTML .= '<div class="table-responsive"><table class="table table-striped table-bordered table-hover">';
835
836             $
this->HTML .= '<thead><tr>';
837             
if(!$Print_x) $this->HTML .= '<th style="width: 18px;" class="text-center"><input class="hidden-print" type="checkbox" title="' . html_attr($Translation['Select all records']) . '" id="select_all_records"></th>';
838         
// Templates
839             $rowTemplate = $selrowTemplate =
'';
840             
if($this->Template){
841                 $rowTemplate = @file_get_contents(
'./' . $this->Template);
842                 
if($rowTemplate && $this->SelectedTemplate){
843                     $selrowTemplate = @file_get_contents(
'./' . $this->SelectedTemplate);
844                 }
845             }
846
847             
// process translations
848             
if($rowTemplate){
849                 
foreach($Translation as $symbol=>$trans){
850                     $rowTemplate=str_replace(
"<%%TRANSLATION($symbol)%%>", $trans, $rowTemplate);
851                 }
852             }
853             
if($selrowTemplate){
854                 
foreach($Translation as $symbol=>$trans){
855                     $selrowTemplate=str_replace(
"<%%TRANSLATION($symbol)%%>", $trans, $selrowTemplate);
856                 }
857             }
858         
// End of templates
859
860         
// $this->ccffv: map $FilterField values to field captions as stored in ColCaption
861             $
this->ccffv = array();
862             
foreach($this->ColCaption as $captionIndex => $caption){
863                 $ffv =
1;
864                 
foreach($this->QueryFieldsFilters as $uselessKey => $filterCaption){
865                     
if($caption == $filterCaption){
866                         $
this->ccffv[$captionIndex] = $ffv;
867                     }
868                     $ffv++;
869                 }
870             }
871
872         
// display table headers
873             $forceHeaderWidth =
false;
874             
if($rowTemplate=='' || $this->ShowTableHeader){
875                 
for($i = 0; $i < count($this->ColCaption); $i++){
876                     
/* Sorting icon and link */
877                     $sort1 = $sort2 = $filterHint =
'';
878                     
if($this->AllowSorting == 1){
879                         
if($current_view != 'TVP'){
880                             $sort1 =
"<a href=\"{$this->ScriptFileName}?SortDirection=asc&SortField=".($this->ColNumber[$i])."\" onClick=\"$resetSelection document.myform.NoDV.value=1; document.myform.SortDirection.value='asc'; document.myform.SortField.value = '".($this->ColNumber[$i])."'; document.myform.submit(); return false;\" class=\"TableHeader\">";
881                             $sort2 =
"</a>";
882                         }
883                         
if($this->ColNumber[$i] == $SortField){
884                             $SortDirection = ($SortDirection ==
"asc" ? "desc" : "asc");
885                             
if($current_view != 'TVP')
886                                 $sort1 =
"<a href=\"{$this->ScriptFileName}?SortDirection=$SortDirection&SortField=".($this->ColNumber[$i])."\" onClick=\"$resetSelection document.myform.NoDV.value=1; document.myform.SortDirection.value='$SortDirection'; document.myform.SortField.value = ".($this->ColNumber[$i])."; document.myform.submit(); return false;\" class=\"TableHeader\">";
887                             $sort2 =
" <i class=\"text-warning glyphicon glyphicon-sort-by-attributes" . ($SortDirection == 'desc' ? '' : '-alt') . "\"></i>{$sort2}";
888                             $SortDirection = ($SortDirection ==
"asc" ? "desc" : "asc");
889                         }
890                     }
891
892                     
/* Filtering icon and hint */
893                     
if($this->AllowFilters && is_array($FilterField)){
894                         
// check to see if there is any filter applied on the current field
895                         
if(isset($this->ccffv[$i]) && in_array($this->ccffv[$i], $FilterField)){
896                             
// render filter icon
897                             $filterHint =
'&nbsp;<button type="submit" class="btn btn-default btn-xs' . ($current_view == 'TVP' ? ' disabled' : '') . '" name="Filter_x" value="1" title="'.html_attr($Translation['filtered field']).'"><i class="glyphicon glyphicon-filter"></i></button>';
898                         }
899                     }
900
901                     $
this->HTML .= "\t<th class=\"{$this->TableName}-{$this->ColFieldName[$i]}\" " . ($forceHeaderWidth ? ' style="width: ' . ($this->ColWidth[$i] ? $this->ColWidth[$i] : 100) . 'px;"' : '') . ">{$sort1}{$this->ColCaption[$i]}{$sort2}{$filterHint}</th>\n";
902                 }
903             }elseif($current_view !=
'TVP'){
904                 
// Display a Sort by drop down
905                 $
this->HTML .= "\t<th class=\"hidden-print\" colspan=\"" . (count($this->ColCaption)) . "\">";
906                 $
this->HTML .= "\t<div class=\"pull-right\" id=\"order-by-selector\">";
907
908                 
if($this->AllowSorting == 1){
909                     $sortCombo =
new Combo;
910                     
for($i=0; $i < count($this->ColCaption); $i++){
911                         $sortCombo->ListItem[] = $
this->ColCaption[$i];
912                         $sortCombo->ListData[] = $
this->ColNumber[$i];
913                     }
914                     $sortCombo->SelectName =
"FieldsList";
915                     $sortCombo->SelectedData = $SortField;
916                     $sortCombo->Class =
'';
917                     $sortCombo->SelectedClass =
'';
918                     $sortCombo->Render();
919                     $sortby_dropdown = $sortCombo->HTML;
920                     $sortby_dropdown = str_replace(
'<select ', "<select onChange=\"document.myform.SortDirection.value='$SortDirection'; document.myform.SortField.value=document.myform.FieldsList.value; document.myform.NoDV.value=1; document.myform.submit();\" ", $sortby_dropdown);
921                     
if($SortField){
922                         $SortDirection = ($SortDirection ==
'desc' ? 'asc' : 'desc');
923                         $sort_class = ($SortDirection ==
'asc' ? 'sort-by-attributes-alt' : 'sort-by-attributes');
924                         $sort =
"<a href=\"javascript: document.myform.NoDV.value=1; document.myform.SortDirection.value='{$SortDirection}'; document.myform.SortField.value='{$SortField}'; document.myform.submit();\" class=TableHeader><i class=\"text-warning glyphicon glyphicon-{$sort_class}\"></i></a>";
925                         $SortDirection = ($SortDirection ==
'desc' ? 'asc' : 'desc');
926                     }
else{
927                         $sort =
'';
928                     }
929
930                     $sortby_sep =
'<span class="hspacer-md"></span>';
931
932                     $
this->HTML .= "{$Translation['order by']}{$sortby_sep}{$sortby_dropdown}{$sortby_sep}{$sort}{$sortby_sep}";
933                 }
934                 $
this->HTML .= "</div><style>#s2id_FieldsList{ min-width: 12em; width: unset !important; }</style></th>\n";
935             }
936
937         
// table view navigation code ...
938             
if($RecordCount && $this->AllowNavigation && $RecordCount>$this->RecordsPerPage){
939                 
while($FirstRecord > $RecordCount)
940                     $FirstRecord -= $
this->RecordsPerPage;
941
942                 
if($FirstRecord == '' || $FirstRecord < 1) $FirstRecord = 1;
943
944                 
if($Previous_x != ''){
945                     $FirstRecord -= $
this->RecordsPerPage;
946                     
if($FirstRecord <= 0)
947                         $FirstRecord =
1;
948                 }elseif($Next_x !=
''){
949                     $FirstRecord += $
this->RecordsPerPage;
950                     
if($FirstRecord > $RecordCount)
951                         $FirstRecord = $RecordCount - ($RecordCount % $
this->RecordsPerPage) + 1;
952                     
if($FirstRecord > $RecordCount)
953                         $FirstRecord = $RecordCount - $
this->RecordsPerPage + 1;
954                     
if($FirstRecord <= 0)
955                         $FirstRecord =
1;
956                 }
957
958             }elseif($RecordCount){
959                 $FirstRecord =
1;
960                 $
this->RecordsPerPage = 2000; // a limit on max records in print preview to avoid performance drops
961             }
962         
// end of table view navigation code
963             $
this->HTML .= "\n\t</tr>\n\n</thead>\n\n<tbody><!-- tv data below -->\n";
964
965             $i =
0;
966             
if($RecordCount){
967                 $i = $FirstRecord;
968             
// execute query for table view
969                 $query_fields = array();
970                 
foreach($this->QueryFieldsTV as $fn => $fc)
971                     $query_fields[] =
"$fn as `$fc`";
972                 $fieldList = implode(
', ', $query_fields);
973
974                 
if($this->PrimaryKey)
975                     $fieldList .=
", $this->PrimaryKey as '" . str_replace('`', '', $this->PrimaryKey) . "'";
976
977                 $tvQuery =
"SELECT {$fieldList} from {$this->QueryFrom} {$this->QueryWhere} {$this->QueryOrder}";
978                 $result = sql($tvQuery .
" limit " . ($i-1) . ",{$this->RecordsPerPage}", $eo);
979                 
while(($row = db_fetch_array($result)) && ($i < ($FirstRecord + $this->RecordsPerPage))){
980                     
/* skip displaying the current record if we're in TVP or multiple DVP and the record is not checked */
981                     
if(($PrintTV || $Print_x) && count($record_selector) && !in_array($row[$FieldCountTV], $record_selector)) continue;
982
983                     $attr_id = html_attr($row[$FieldCountTV]);
/* pk value suitable for inserting into html tag attributes */
984                     $js_id = addslashes($row[$FieldCountTV]);
/* pk value suitable for inserting into js strings */
985
986                     
/* show record selector except in TVP */
987                     
if($Print_x != ''){ $this->HTML .= '<tr>'; }
988
989                     
if(!$Print_x){
990                         $
this->HTML .= ($SelectedID == $row[$FieldCountTV] ? '<tr class="active">' : '<tr>');
991                         $
checked = (is_array($record_selector) && in_array($row[$FieldCountTV], $record_selector) ? ' checked' : '');
992                         $
this->HTML .= "<td class=\"text-center\"><input class=\"hidden-print record_selector\" type=\"checkbox\" id=\"record_selector_{$attr_id}\" name=\"record_selector[]\" value=\"{$attr_id}\"{$checked}></td>";
993                     }
994
995                     
/* apply record templates */
996                     
if($rowTemplate != ''){
997                         $rowTemp = $rowTemplate;
998                         
if($this->AllowSelection == 1 && $SelectedID == $row[$FieldCountTV] && $selrowTemplate != ''){
999                             $rowTemp = $selrowTemplate;
1000                         }
1001
1002                         
if($this->AllowSelection == 1 && $SelectedID != $row[$FieldCountTV]){
1003                             $rowTemp = str_replace(
'<%%SELECT%%>',"<a onclick=\"document.myform.SelectedField.value=this.parentNode.cellIndex; document.myform.SelectedID.value='" . addslashes($row[$FieldCountTV]) . "'; document.myform.submit(); return false;\" href=\"{$this->ScriptFileName}?SelectedID=" . html_attr($row[$FieldCountTV]) . "\" style=\"display: block; padding:0px;\">",$rowTemp);
1004                             $rowTemp = str_replace(
'<%%ENDSELECT%%>','</a>',$rowTemp);
1005                         }
else{
1006                             $rowTemp = str_replace(
'<%%SELECT%%>', '', $rowTemp);
1007                             $rowTemp = str_replace(
'<%%ENDSELECT%%>', '', $rowTemp);
1008                         }
1009
1010                         
for($j = 0; $j < $FieldCountTV; $j++){
1011                             $fieldTVCaption = current(array_slice($
this->QueryFieldsTV, $j, 1));
1012
1013                             $fd = safe_html($row[$j]);
1014
1015                             
/*
1016                                 the TV template could contain field placeholders
in the format
1017                                 <%%FIELD_n%%> or <%%VALUE(Field caption)%%> or <%%HTML_ATTR(field caption)%%>
1018                             */

1019                             $rowTemp = str_replace(
"<%%FIELD_$j%%>", thisOr($fd, ''), $rowTemp);
1020                             $rowTemp = str_replace(
"<%%VALUE($fieldTVCaption)%%>", thisOr($fd, ''), $rowTemp);
1021                             $rowTemp = str_replace(
"<%%HTML_ATTR($fieldTVCaption)%%>", html_attr($fd), $rowTemp);
1022
1023                             
if(strpos($rowTemp, "<%%YOUTUBETHUMB($fieldTVCaption)%%>") !== false) $rowTemp = str_replace("<%%YOUTUBETHUMB($fieldTVCaption)%%>", thisOr(get_embed('youtube', $fd, '', '', 'thumbnail_url'), 'blank.gif'), $rowTemp);
1024                             
if(strpos($rowTemp, "<%%GOOGLEMAPTHUMB($fieldTVCaption)%%>") !== false) $rowTemp = str_replace("<%%GOOGLEMAPTHUMB($fieldTVCaption)%%>", thisOr(get_embed('googlemap', $fd, '', '', 'thumbnail_url'), 'blank.gif'), $rowTemp);
1025                             
if(thisOr($fd)=='&nbsp;' && preg_match('/<a href=".*?&nbsp;.*?<\/a>/i', $rowTemp, $m)){
1026                                 $rowTemp=str_replace($m[
0], '', $rowTemp);
1027                             }
1028                         }
1029
1030                         $
this->HTML .= $rowTemp;
1031                         $rowTemp =
'';
1032
1033                     }
else{
1034                         
// default view if no template
1035                         
for($j = 0; $j < $FieldCountTV; $j++){
1036                             
if($this->AllowSelection == 1){
1037                                 $sel1 =
"<a href=\"{$this->ScriptFileName}?SelectedID=" . html_attr($row[$FieldCountTV]) . "\" onclick=\"document.myform.SelectedID.value='" . addslashes($row[$FieldCountTV]) . "'; document.myform.submit(); return false;\" style=\"padding:0px;\">";
1038                                 $sel2 =
"</a>";
1039                             }
else{
1040                                 $sel1 =
'';
1041                                 $sel2 =
'';
1042                             }
1043
1044                             $
this->HTML .= "<td valign=\"top\"><div>&nbsp;{$sel1}{$row[$j]}{$sel2}&nbsp;</div></td>";
1045                         }
1046                     }
1047                     $
this->HTML .= "</tr>\n";
1048                     $i++;
1049                 }
1050                 $i--;
1051             }
1052
1053             $
this->HTML = preg_replace("/<a href=\"(mailto:)?&nbsp;[^\n]*title=\"&nbsp;\"><\/a>/", '&nbsp;', $this->HTML);
1054             $
this->HTML = preg_replace("/<a [^>]*>(&nbsp;)*<\/a>/", '&nbsp;', $this->HTML);
1055             $
this->HTML = preg_replace("/<%%.*%%>/U", '&nbsp;', $this->HTML);
1056         
// end of data
1057             $
this->HTML.='<!-- tv data above -->';
1058             $
this->HTML .= "\n</tbody>";
1059
1060             
if($Print_x == ''){ // TV
1061                 $pagesMenu =
'';
1062                 
if($RecordCount > $this->RecordsPerPage){
1063                     $pagesMenuId =
"{$this->TableName}_pagesMenu";
1064                     $pagesMenu = $Translation[
'go to page'] . ' <select style="width: 90%; max-width: 8em;" class="input-sm ltr" id="' . $pagesMenuId . '" onChange="document.myform.writeAttribute(\'novalidate\', \'novalidate\'); document.myform.NoDV.value=1; document.myform.FirstRecord.value=(this.value * ' . $this->RecordsPerPage . '+1); document.myform.submit();">';
1065                     $pagesMenu .=
'</select>';
1066
1067                     $pagesMenu .=
'<script>';
1068                     $pagesMenu .=
'var lastPage = ' . (ceil($RecordCount / $this->RecordsPerPage) - 1) . ';';
1069                     $pagesMenu .=
'var currentPage = ' . (($FirstRecord - 1) / $this->RecordsPerPage) . ';';
1070                     $pagesMenu .=
'var pagesMenu = document.getElementById("' . $pagesMenuId . '");';
1071                     $pagesMenu .=
'var lump = ' . datalist_max_page_lump . ';';
1072
1073                     $pagesMenu .=
'if(lastPage <= lump * 3){';
1074                     $pagesMenu .=
' addPageNumbers(0, lastPage);';
1075                     $pagesMenu .=
'}else{';
1076                     $pagesMenu .=
' addPageNumbers(0, lump - 1);';
1077                     $pagesMenu .=
' if(currentPage < lump) addPageNumbers(lump, currentPage + lump / 2);';
1078                     $pagesMenu .=
' if(currentPage >= lump && currentPage < (lastPage - lump)){';
1079                     $pagesMenu .=
' addPageNumbers(';
1080                     $pagesMenu .=
' Math.max(currentPage - lump / 2, lump),';
1081                     $pagesMenu .=
' Math.min(currentPage + lump / 2, lastPage - lump - 1)';
1082                     $pagesMenu .=
' );';
1083                     $pagesMenu .=
' }';
1084                     $pagesMenu .=
' if(currentPage >= (lastPage - lump)) addPageNumbers(currentPage - lump / 2, lastPage - lump - 1);';
1085                     $pagesMenu .=
' addPageNumbers(lastPage - lump, lastPage);';
1086                     $pagesMenu .=
'}';
1087
1088                     $pagesMenu .=
'function addPageNumbers(fromPage, toPage){';
1089                     $pagesMenu .=
' var ellipsesIndex = 0;';
1090                     $pagesMenu .=
' if(fromPage > toPage) return;';
1091                     $pagesMenu .=
' if(fromPage > 0){';
1092                     $pagesMenu .=
' if(pagesMenu.options[pagesMenu.options.length - 1].text != fromPage){';
1093                     $pagesMenu .=
' ellipsesIndex = pagesMenu.options.length;';
1094                     $pagesMenu .=
' fromPage--;';
1095                     $pagesMenu .=
' }';
1096                     $pagesMenu .=
' }';
1097                     $pagesMenu .=
' for(i = fromPage; i <= toPage; i++){';
1098                     $pagesMenu .=
' var option = document.createElement("option");';
1099                     $pagesMenu .=
' option.text = (i + 1);';
1100                     $pagesMenu .=
' option.value = i;';
1101                     $pagesMenu .=
' if(i == currentPage){ option.selected = "selected"; }';
1102                     $pagesMenu .=
' try{';
1103                     $pagesMenu .=
' /* for IE earlier than version 8 */';
1104                     $pagesMenu .=
' pagesMenu.add(option, pagesMenu.options[null]);';
1105                     $pagesMenu .=
' }catch(e){';
1106                     $pagesMenu .=
' pagesMenu.add(option, null);';
1107                     $pagesMenu .=
' }';
1108                     $pagesMenu .=
' }';
1109                     $pagesMenu .=
' if(ellipsesIndex > 0){';
1110                     $pagesMenu .=
' pagesMenu.options[ellipsesIndex].text = " ... ";';
1111                     $pagesMenu .=
' }';
1112                     $pagesMenu .=
'}';
1113                     $pagesMenu .=
'</script>';
1114                 }
1115
1116                 $
this->HTML .= "\n\t";
1117
1118                 
if($i){ // 1 or more records found
1119                     $
this->HTML .= "<tfoot><tr><td colspan=".(count($this->ColCaption)+1).'>';
1120                         $
this->HTML .= $Translation['records x to y of z'];
1121                     $
this->HTML .= '</td></tr></tfoot>';
1122                 }
1123
1124                 
if(!$i){ // no records found
1125                     $
this->HTML .= "<tfoot><tr><td colspan=".(count($this->ColCaption)+1).'>';
1126                         $
this->HTML .= '<div class="alert alert-warning">';
1127                             $
this->HTML .= '<i class="glyphicon glyphicon-warning-sign"></i> ';
1128                             $
this->HTML .= $Translation['No matches found!'];
1129                         $
this->HTML .= '</div>';
1130                     $
this->HTML .= '</td></tr></tfoot>';
1131                 }
1132
1133             }
else{ // TVP
1134                 
if($i) $this->HTML .= "\n\t<tfoot><tr><td colspan=".(count($this->ColCaption) + 1). '>' . $Translation['records x to y of z'] . '</td></tr></tfoot>';
1135                 
if(!$i) $this->HTML .= "\n\t<tfoot><tr><td colspan=".(count($this->ColCaption) + 1). '>' . $Translation['No matches found!'] . '</td></tr></tfoot>';
1136             }
1137
1138             $
this->HTML = str_replace("<FirstRecord>", number_format($FirstRecord), $this->HTML);
1139             $
this->HTML = str_replace("<LastRecord>", number_format($i), $this->HTML);
1140             $
this->HTML = str_replace("<RecordCount>", number_format($RecordCount), $this->HTML);
1141             $tvShown=
true;
1142
1143             $
this->HTML .= "</table></div>\n";
1144
1145             
/* highlight quick search matches */
1146             
if($SearchString!='') $this->HTML .= '<script>$j(function(){ $j(".table-responsive td").mark("' . html_attr($SearchString) . '", { className: "text-warning bg-warning", diacritics: false }); })</script>';
1147
1148             
if($Print_x == '' && $i){ // TV
1149                 $
this->HTML .= '<div class="row pagination-section">';
1150                     $
this->HTML .= '<div class="col-xs-4 col-md-3 col-lg-2 vspacer-lg">';
1151                         $
this->HTML .= '<button onClick="' . $resetSelection . ' document.myform.NoDV.value = 1; return true;" type="submit" name="Previous_x" id="Previous" value="1" class="btn btn-default btn-block"><i class="glyphicon glyphicon-chevron-left"></i> <span class="hidden-xs">' . $Translation['Previous'] . '</span></button>';
1152                     $
this->HTML .= '</div>';
1153
1154                     $
this->HTML .= '<div class="col-xs-4 col-md-4 col-lg-2 col-md-offset-1 col-lg-offset-3 text-center vspacer-lg">';
1155                         $
this->HTML .= $pagesMenu;
1156                     $
this->HTML .= '</div>';
1157
1158                     $
this->HTML .= '<div class="col-xs-4 col-md-3 col-lg-2 col-md-offset-1 col-lg-offset-3 text-right vspacer-lg">';
1159                         $
this->HTML .= '<button onClick="'.$resetSelection.' document.myform.NoDV.value=1; return true;" type="submit" name="Next_x" id="Next" value="1" class="btn btn-default btn-block"><span class="hidden-xs">' . $Translation['Next'] . '</span> <i class="glyphicon glyphicon-chevron-right"></i></button>';
1160                     $
this->HTML .= '</div>';
1161                 $
this->HTML .= '</div>';
1162             }
1163
1164             $
this->HTML .= '</div>'; // end of div.table_view
1165         }
1166         
/* that marks the end of the TV table */
1167
1168     
// hidden variables ....
1169         
foreach($this->filterers as $filterer => $caption){
1170             
if($_REQUEST['filterer_' . $filterer] != ''){
1171                 $
this->HTML .= "<input name=\"filterer_{$filterer}\" value=\"" . html_attr($_REQUEST['filterer_' . $filterer]) . "\" type=\"hidden\" />";
1172                 
break; // currently, only one filterer can be applied at a time
1173             }
1174         }
1175
1176         $
this->HTML .= '<!-- possible values for current_view: TV, TVP, DV, DVP, Filters, TVDV -->';
1177         $
this->HTML .= '<input name="current_view" id="current_view" value="' . $current_view . '" type="hidden">';
1178         $
this->HTML .= '<input name="SortField" value="' . $SortField . '" type="hidden">';
1179         $
this->HTML .= '<input name="SelectedID" value="' . html_attr($SelectedID) . '" type="hidden">';
1180         $
this->HTML .= '<input name="SelectedField" value="" type="hidden">';
1181         $
this->HTML .= '<input name="SortDirection" type="hidden" value="' . $SortDirection . '">';
1182         $
this->HTML .= '<input name="FirstRecord" type="hidden" value="' . $FirstRecord . '">';
1183         $
this->HTML .= '<input name="NoDV" type="hidden" value="">';
1184         $
this->HTML .= '<input name="PrintDV" type="hidden" value="">';
1185         
if($this->QuickSearch && !strpos($this->HTML, 'SearchString')) $this->HTML .= '<input name="SearchString" type="hidden" value="' . html_attr($SearchString) . '">';
1186     
// hidden variables: filters ...
1187         $FiltersCode =
'';
1188         
for($i = 1; $i <= (datalist_filters_count * $FiltersPerGroup); $i++){ // Number of filters allowed
1189             
if($i%$FiltersPerGroup == 1 && $i != 1 && $FilterAnd[$i] != ''){
1190                 $FiltersCode .=
"<input name=\"FilterAnd[$i]\" value=\"$FilterAnd[$i]\" type=\"hidden\">\n";
1191             }
1192             
if($FilterField[$i] != '' && $FilterOperator[$i] != '' && ($FilterValue[$i] != '' || strpos($FilterOperator[$i], 'empty'))){
1193                 
if(!strstr($FiltersCode, "<input name=\"FilterAnd[{$i}]\" value="))
1194                     $FiltersCode .=
"<input name=\"FilterAnd[{$i}]\" value=\"{$FilterAnd[$i]}\" type=\"hidden\">\n";
1195                 $FiltersCode .=
"<input name=\"FilterField[{$i}]\" value=\"{$FilterField[$i]}\" type=\"hidden\">\n";
1196                 $FiltersCode .=
"<input name=\"FilterOperator[{$i}]\" value=\"{$FilterOperator[$i]}\" type=\"hidden\">\n";
1197                 $FiltersCode .=
"<input name=\"FilterValue[{$i}]\" value=\"" . html_attr($FilterValue[$i]) . "\" type=\"hidden\">\n";
1198             }
1199         }
1200         $FiltersCode .=
"<input name=\"DisplayRecords\" value=\"$DisplayRecords\" type=\"hidden\" />";
1201         $
this->HTML .= $FiltersCode;
1202
1203     
// display details form ...
1204         
if(($this->AllowSelection || $this->AllowInsert || $this->AllowUpdate || $this->AllowDelete) && $Print_x=='' && !$PrintDV){
1205             
if(($this->SeparateDV && $this->HideTableView) || !$this->SeparateDV){
1206                 $dvCode = call_user_func(
"{$this->TableName}_form", $SelectedID, $this->AllowUpdate, (($this->HideTableView && $SelectedID) ? 0 : $this->AllowInsert), $this->AllowDelete, $this->SeparateDV, $this->TemplateDV, $this->TemplateDVP);
1207
1208                 $
this->HTML .= "\n\t<div class=\"col-xs-12 detail_view {$this->DVClasses}\">{$tv_dv_separator}<div class=\"panel panel-default\">{$dvCode}</div></div>";
1209                 $
this->HTML .= ($this->SeparateDV ? '<input name="SearchString" value="' . html_attr($SearchString) . '" type="hidden">' : '');
1210                 
if($dvCode){
1211                     $
this->ContentType = 'detailview';
1212                     $dvShown =
true;
1213                 }
1214             }
1215         }
1216
1217     
// display multiple printable detail views
1218         
if($PrintDV){
1219             $dvCode =
'';
1220             $_REQUEST[
'dvprint_x'] = 1;
1221
1222             
// hidden vars
1223             
foreach($this->filterers as $filterer => $caption){
1224                 
if($_REQUEST['filterer_' . $filterer] != ''){
1225                     $
this->HTML .= "<input name=\"filterer_{$filterer}\" value=\"" . html_attr($_REQUEST['filterer_' . $filterer]) . "\" type=\"hidden\" />";
1226                     
break; // currently, only one filterer can be applied at a time
1227                 }
1228             }
1229
1230             
// count selected records
1231             $selectedRecords =
0;
1232             
if(is_array($record_selector)) foreach($record_selector as $id){
1233                 $selectedRecords++;
1234                 $
this->HTML .= '<input type="hidden" name="record_selector[]" value="' . html_attr($id) . '">'."\n";
1235             }
1236
1237             
if($selectedRecords && $selectedRecords <= datalist_max_records_dv_print){ // if records selected > {datalist_max_records_dv_print} don't show DV preview to avoid db performance issues.
1238                 
foreach($record_selector as $id){
1239                     $dvCode .= call_user_func($
this->TableName . '_form', $id, 0, 0, 0, 1, $this->TemplateDV, $this->TemplateDVP);
1240                 }
1241
1242                 
if($dvCode!=''){
1243                     $dvCode = preg_replace(
'/<input .*?type="?image"?.*?>/', '', $dvCode);
1244                     $
this->HTML .= $dvCode;
1245                 }
1246             }
else{
1247                 $
this->HTML .= error_message($Translation['Maximum records allowed to enable this feature is'] . ' ' . datalist_max_records_dv_print);
1248                 $
this->HTML .= '<input type="submit" class="print-button" value="'.$Translation['Print Preview Table View'].'">';
1249             }
1250         }
1251
1252         $
this->HTML .= "</div></form>";
1253         $
this->HTML .= '</div><div class="col-xs-1 md-hidden lg-hidden"></div></div>';
1254
1255         
// $this->HTML .= '<font face="garamond">'.html_attr($tvQuery).'</font>'; // uncomment this line for debugging the table view query
1256
1257         
if($dvShown && $tvShown) $this->ContentType='tableview+detailview';
1258         
if($dvprint_x!='') $this->ContentType='print-detailview';
1259         
if($Print_x!='') $this->ContentType='print-tableview';
1260         
if($PrintDV!='') $this->ContentType='print-detailview';
1261
1262         
// call detail view javascript hook file if found
1263         $dvJSHooksFile=dirname(__FILE__).
'/hooks/'.$this->TableName.'-dv.js';
1264         
if(is_file($dvJSHooksFile) && ($this->ContentType=='detailview' || $this->ContentType=='tableview+detailview')){
1265             $
this->HTML.="\n<script src=\"hooks/{$this->TableName}-dv.js\"></script>\n";
1266         }
1267
1268         $
this->set_headers();
1269         
return;
1270     }
1271
1272     function validate_filters($req, $FiltersPerGroup =
4, $is_gpc = true){
1273         $fand = (isset($req[
'FilterAnd']) && is_array($req['FilterAnd']) ? $req['FilterAnd'] : array());
1274         $ffield = (isset($req[
'FilterField']) && is_array($req['FilterField']) ? $req['FilterField'] : array());
1275         $fop = (isset($req[
'FilterOperator']) && is_array($req['FilterOperator']) ? $req['FilterOperator'] : array());
1276         $fvalue = (isset($req[
'FilterValue']) && is_array($req['FilterValue']) ? $req['FilterValue'] : array());
1277
1278         
/* make sure FilterAnd is either 'and' or 'or' */
1279         
foreach($fand as $i => $f){
1280             
if($f && !preg_match('/^(and|or)$/i', trim($f))) $fand[$i] = 'and';
1281         }
1282
1283         
/* FilterField must be a positive integer */
1284         
foreach($ffield as $ffi => $ffn){
1285             $ffield[$ffi] = max(
0, intval($ffn));
1286         }
1287
1288         
/* validate FilterOperator */
1289         
foreach($fop as $i => $f){
1290             $fop[$i] = trim($f);
1291             
if($f && !in_array(trim($f), array_keys($GLOBALS['filter_operators']))){
1292                 $fop[$i] =
'';
1293             }
1294         }
1295
1296         
/* undo magic quotes if gpc */
1297         
if($is_gpc){
1298             
foreach($fvalue as $fvi => $fv){
1299                 $fvalue[$fvi] = (get_magic_quotes_gpc() ? stripslashes($fv) : $fv);
1300             }
1301         }
1302
1303         
/* clear fand, ffield and fop for filters having no value or no field */
1304         
/* assume equal-to op and 'and' if missing */
1305         
for($i = 1; $i <= datalist_filters_count * $FiltersPerGroup; $i++){
1306             
if(!isset($fand[$i]) && !isset($ffield[$i]) && !isset($fop[$i]) && !isset($fvalue[$i])) continue;
1307
1308             
if(($fvalue[$i] == '' && !in_array($fop[$i], array('is-empty', 'is-not-empty'))) || !$ffield[$i]){
1309                 unset($ffield[$i], $fop[$i], $fvalue[$i]);
1310                 
if($i % $FiltersPerGroup != 1) unset($fand[$i]);
1311             }
else{
1312                 
if(!$fand[$i]) $fand[$i] = 'and';
1313                 
if(!$fop[$i]) $fop[$i] = 'equal-to';
1314             }
1315         }
1316
1317         
/* empty FilterAnd for empty groups or set to 'and' if empty while group not empty */
1318         
for($i = 1; $i <= datalist_filters_count * $FiltersPerGroup; $i += $FiltersPerGroup){
1319             $empty_group =
true;
1320
1321             
for($j = $i; $j < ($i + $FiltersPerGroup); $j++){
1322                 
if($ffield[$j]) $empty_group = false;
1323             }
1324
1325             
if($empty_group){
1326                 $fand[$i] =
'';
1327                 
continue;
1328             }
1329
1330             
if(!$fand[$i]) $fand[$i] = 'and';
1331         }
1332
1333         
return array($fand, $ffield, $fop, $fvalue);
1334     }
1335
1336     
/**
1337      * @brief Returns HTML/JS code
for displaying TV table options (hide/show columns)
1338      */

1339     function tv_tools(){
1340         
global $Translation;
1341
1342         ob_start();
1343         ?>
1344
1345         <?php
if($this->ShowTableHeader){ ?>
1346             <div
class="pull-right flip btn-group vspacer-md tv-tools">
1347                 <button title=
"<?php echo html_attr($Translation['hide/show columns']); ?>" type="button" class="btn btn-default tv-toggle" data-toggle="collapse" data-target="#toggle-columns-container"><i class="glyphicon glyphicon-align-justify rotate90"></i></button>
1348             </div>
1349         <?php } ?>
1350
1351         <div
class="pull-right flip btn-group vspacer-md hspacer-md tv-tools">
1352             <button title=
"<?php echo html_attr($Translation['previous column']); ?>" type="button" class="btn btn-default tv-scroll" onclick="AppGini.TVScroll().less()"><i class="glyphicon glyphicon-step-backward"></i></button>
1353             <button title=
"<?php echo html_attr($Translation['next column']); ?>" type="button" class="btn btn-default tv-scroll" onclick="AppGini.TVScroll().more()"><i class="glyphicon glyphicon-step-forward"></i></button>
1354         </div>
1355         <div
class="clearfix"></div>
1356
1357         <?php
if($this->ShowTableHeader){ ?>
1358             <div
class="collapse" id="toggle-columns-container">
1359                 <div
class="well pull-right flip" style="width: 100%; max-width: 600px;">
1360                     <div
class="row" id="toggle-columns">
1361                         <div
class="col-md-12">
1362                             <div
class="btn-group" style="width: 100%;">
1363                                 <button type=
"button" class="btn btn-default" id="show-all-columns" style="width: 33.3%;"><i class="glyphicon glyphicon-check"></i> <?php echo $Translation['Reset Filters']; ?></button>
1364                                 <button type=
"button" class="btn btn-default" id="hide-all-columns" style="width: 33.3%;"><i class="glyphicon glyphicon-unchecked"></i> <?php echo $Translation['hide all']; ?></button>
1365                                 <button type=
"button" class="btn btn-default" id="toggle-columns-checks" style="width: 33.4%;"><i class="glyphicon glyphicon-random"></i> <?php echo $Translation['toggle']; ?></button>
1366                             </div>
1367                         </div>
1368                         <div
class="col-md-12"><button type="button" class="btn btn-default btn-block" id="toggle-columns-collapser" data-toggle="collapse" data-target="#toggle-columns-container"><i class="glyphicon glyphicon-ok"></i> <?php echo $Translation['ok']; ?></button></div>
1369                     </div>
1370                 </div>
1371                 <div
class="clearfix"></div>
1372             </div>
1373         <?php } ?>
1374
1375         <script>
1376             $j(function(){
1377                 
/**
1378                  * @brief Saves/retrieves
value of column toggle status
1379                  *
1380                  * @param [
in] col_class class of column concerned
1381                  * @param [
in] val boolean, optional value to save.
1382                  * @
return column toggle status if no value is passed
1383                  */

1384                 
var col_cookie = function(col_class, val){
1385                     
if(col_class === undefined) return true;
1386                     
if(val !== undefined && val !== true && val !== false) val = true;
1387
1388                     
var cn = 'columns-' + location.pathname.split(/\//).pop().split(/\./).shift(); // cookie name
1389                     
var op = { expires: 30, path: '' }; // cookie options
1390                     
var c = Cookies.getJSON(cn) || {};
1391
1392                     
/* if no cookie, create it and set it to val (or true if no val) */
1393                     
if(c[col_class] === undefined){
1394                         
if(val === undefined) val = true;
1395
1396                         c[col_class] = val;
1397                         Cookies.
set(cn, c, op);
1398                         
return val;
1399                     }
1400
1401                     
/* if cookie found and val provided, set cookie to new val */
1402                     
if(val !== undefined){
1403                         c[col_class] = val;
1404                         Cookies.
set(cn, c, op);
1405                         
return val;
1406                     }
1407
1408                     
/* if cookie found and no val, return cookie val */
1409                     
return c[col_class];
1410                 }
1411
1412                 
/**
1413                  * @brief shows/hides column given its
class, and saves this into cookies
1414                  *
1415                  * @param [
in] col_class class of column to show/hide
1416                  * @param [
in] show boolean, optional. Set to false to hide. Default is true (to show).
1417                  */

1418                 
var show_column = function(col_class, show){
1419                     
if(col_class == undefined) return;
1420                     
if(show == undefined) show = true;
1421
1422                     
if(show === false) $j('.' + col_class).hide();
1423                     
else $j('.' + col_class).show();
1424
1425                     AppGini.TVScroll().reset();
1426
1427                     col_cookie(col_class, show);
1428                 }
1429
1430                 
/* initiate TVScroll */
1431                 AppGini.TVScroll().less();
1432
1433             <?php
if($this->ShowTableHeader){ ?>
1434                 
/* handle toggling columns' checkboxes */
1435                 $j(
'#toggle-columns-container').on('click', 'input[type=checkbox]', function(){
1436                     show_column($j(
this).data('col'), $j(this).prop('checked'));
1437                 });
1438
1439                 
/* get TV columns and populate the #toggle-columns section */
1440                 $j(
'.table_view th').each(function(){
1441                     
var th = $j(this);
1442
1443                     
/* ignore the record selector column */
1444                     
if(th.find('#select_all_records').length > 0) return;
1445
1446                     
var col_class = th.attr('class');
1447                     
var label = $j.trim(th.text());
1448
1449                     
/* Add a toggler for the column in the #toggle-columns section */
1450                     $j(
1451                         
'<div class="col-md-6"><div class="checkbox"><label>' +
1452                             
'<input type="checkbox" data-col="' + col_class + '" checked> ' + label +
1453                         
'</label></div></div>'
1454                     ).insertBefore(
'#toggle-columns-collapser');
1455
1456                     
/* load saved column status */
1457                     
var col_status = col_cookie(col_class);
1458                     
if(col_status === false) $j('#toggle-columns input[type=checkbox]:last').trigger('click');
1459                 });
1460
1461                 
/* handle clicking 'show all [columns]' */
1462                 $j(
'#show-all-columns').click(function(){
1463                     $j(
'#toggle-columns input[type=checkbox]:not(:checked)').trigger('click');
1464                 });
1465
1466                 
/* handle clicking 'hide all [columns]' */
1467                 $j(
'#hide-all-columns').click(function(){
1468                     $j(
'#toggle-columns input[type=checkbox]:checked').trigger('click');
1469                 });
1470
1471                 
/* handle clicking 'toggle [columns]' */
1472                 $j(
'#toggle-columns-checks').click(function(){
1473                     $j(
'#toggle-columns input[type=checkbox]').trigger('click');
1474                 });
1475             <?php } ?>
1476             })
1477         </script>
1478         <?php
1479         
return ob_get_clean();
1480     }
1481
1482 }



Hệ thống xếp lịch học tín chỉ cho sinh viên CNTT trên PHP & MySQL 111.067 lượt xem

Gõ tìm kiếm nhanh...